/*
 * Copyright (C) 2016  Nicola Spanti (RyDroid) <dev@nicola-spanti.info>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */


#include <check.h>
#include "PlanetWars2dRT-core/specific/model/ship.h"
#include "PlanetWars2dRT-core/specific/controller/ship.h"
#include "check_observable_generic.h"


struct ship_t a_ship;

void
tests_setup_init(void)
{
  struct position_2d_uint position;
  position.x = 0;
  position.y = 0;
  a_ship = ship_create_default(&position, &position);
}

void
tests_setup_free(void)
{
  ship_destructor(&a_ship);
}


START_TEST(test_ship_life_max)
{
  ck_assert_uint_gt(ship_get_life_max(&a_ship), 0);
  ck_assert_uint_le(ship_get_life_current(&a_ship),
		    ship_get_life_max    (&a_ship));
}
END_TEST

START_TEST(test_ship_reduce_life)
{
  a_ship.life_max     = 10;
  
  a_ship.life_current = ship_get_life_max(&a_ship);
  ship_reduce_life(&a_ship, 1);
  ck_assert_uint_eq(ship_get_life_current(&a_ship), 9);
  ck_assert_uint_lt(ship_get_life_current(&a_ship),
		    ship_get_life_max    (&a_ship));
  
  a_ship.life_current = ship_get_life_max(&a_ship);
  ship_reduce_life(&a_ship, 2);
  ck_assert_uint_eq(ship_get_life_current(&a_ship), 8);
  ck_assert_uint_lt(ship_get_life_current(&a_ship),
		    ship_get_life_max    (&a_ship));
  
  a_ship.life_current = ship_get_life_max(&a_ship);
  ship_reduce_life(&a_ship, ship_get_life_max(&a_ship) * 2);
  ck_assert_uint_eq(ship_get_life_current(&a_ship), 0);
  ck_assert_uint_lt(ship_get_life_current(&a_ship),
		    ship_get_life_max    (&a_ship));
  ck_assert(ship_is_destroyed(&a_ship));
}
END_TEST

START_TEST(test_ship_destroy)
{
  ship_destroy(&a_ship);
  ck_assert(ship_is_destroyed(&a_ship));
  ck_assert_uint_eq(ship_get_life_current(&a_ship), 0);
  ck_assert_uint_lt(ship_get_life_current(&a_ship),
		    ship_get_life_max    (&a_ship));
}
END_TEST


void
test_ship_move_x_generic(void (*move_function)(ship_t*))
{
  ship_set_top_left_position_current_x(&a_ship, 10);
  ship_set_top_left_position_to_go_x  (&a_ship, 10);
  ship_set_moving_vector              (&a_ship,  1);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_x(&a_ship), 10);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_x  (&a_ship), 10);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_x(&a_ship), 10);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_x  (&a_ship), 10);
  
  ship_set_top_left_position_current_x(&a_ship, 10);
  ship_set_top_left_position_to_go_x  (&a_ship, 11);
  ship_set_moving_vector              (&a_ship,  1);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_x(&a_ship), 11);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_x  (&a_ship), 11);
  
  ship_set_top_left_position_current_x(&a_ship, 10);
  ship_set_top_left_position_to_go_x  (&a_ship, 12);
  ship_set_moving_vector              (&a_ship,  1);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_x(&a_ship), 11);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_x  (&a_ship), 12);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_x(&a_ship), 12);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_x  (&a_ship), 12);
  
  ship_set_top_left_position_current_x(&a_ship, 10);
  ship_set_top_left_position_to_go_x  (&a_ship,  9);
  ship_set_moving_vector              (&a_ship,  1);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_x(&a_ship), 9);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_x  (&a_ship), 9);
  
  ship_set_top_left_position_current_x(&a_ship, 10);
  ship_set_top_left_position_to_go_x  (&a_ship,  9);
  ship_set_moving_vector              (&a_ship,  2);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_x(&a_ship), 9);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_x  (&a_ship), 9);
  
  ship_set_top_left_position_current_x(&a_ship,  1);
  ship_set_top_left_position_to_go_x  (&a_ship,  0);
  ship_set_moving_vector              (&a_ship, 10);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_x(&a_ship), 0);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_x  (&a_ship), 0);
}
void
test_ship_move_y_generic(void (*move_function)(ship_t*))
{
  ship_set_top_left_position_current_y(&a_ship, 10);
  ship_set_top_left_position_to_go_y  (&a_ship, 10);
  ship_set_moving_vector              (&a_ship,  1);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_y(&a_ship), 10);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_y  (&a_ship), 10);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_y(&a_ship), 10);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_y  (&a_ship), 10);
  
  ship_set_top_left_position_current_y(&a_ship, 10);
  ship_set_top_left_position_to_go_y  (&a_ship, 11);
  ship_set_moving_vector              (&a_ship,  1);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_y(&a_ship), 11);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_y  (&a_ship), 11);
  
  ship_set_top_left_position_current_y(&a_ship, 10);
  ship_set_top_left_position_to_go_y  (&a_ship, 12);
  ship_set_moving_vector              (&a_ship,  1);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_y(&a_ship), 11);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_y  (&a_ship), 12);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_y(&a_ship), 12);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_y  (&a_ship), 12);
  
  ship_set_top_left_position_current_y(&a_ship, 10);
  ship_set_top_left_position_to_go_y  (&a_ship,  9);
  ship_set_moving_vector              (&a_ship,  1);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_y(&a_ship),  9);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_y  (&a_ship),  9);
  
  ship_set_top_left_position_current_y(&a_ship, 10);
  ship_set_top_left_position_to_go_y  (&a_ship,  9);
  ship_set_moving_vector              (&a_ship,  2);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_y(&a_ship),  9);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_y  (&a_ship),  9);
  
  ship_set_top_left_position_current_y(&a_ship,  1);
  ship_set_top_left_position_to_go_y  (&a_ship,  0);
  ship_set_moving_vector              (&a_ship, 10);
  move_function(&a_ship);
  ck_assert_uint_eq(ship_get_top_left_position_current_y(&a_ship),  0);
  ck_assert_uint_eq(ship_get_top_left_position_to_go_y  (&a_ship),  0);
}

START_TEST(test_ship_move_once_x)
{
  test_ship_move_x_generic(ship_move_once_returns_void);
}
END_TEST

START_TEST(test_ship_update_once_move_x)
{
  test_ship_move_x_generic(ship_update_once);
}
END_TEST

START_TEST(test_ship_move_once_y)
{
  test_ship_move_y_generic(ship_move_once_returns_void);
}
END_TEST

START_TEST(test_ship_update_once_move_y)
{
  test_ship_move_y_generic(ship_update_once);
}
END_TEST


START_TEST(test_ship_equals)
{
  ck_assert(ship_equals_except_observers(&a_ship, &a_ship));
  
  ship_destroy(&a_ship);
  ck_assert(ship_equals_except_observers(&a_ship, &a_ship));
  
  struct ship_t ship1, ship2;
  ship1 = a_ship;
  ship2 = a_ship;
  ship_set_top_left_position_current_x(&ship1, 1);
  ship_set_top_left_position_current_x(&ship2, 2);
  ck_assert(!ship_equals_except_observers(&ship1, &ship2));
  ship_set_top_left_position_current_x(&ship1, 0);
  ship_set_top_left_position_current_x(&ship2, 0);
  ck_assert(ship_equals_except_observers(&ship1, &ship2));
  ship_set_top_left_position_current_x(&ship1, 1);
  ship_set_top_left_position_current_x(&ship2, 1);
  ship_set_top_left_position_to_go_x  (&ship1, 1);
  ship_set_top_left_position_to_go_x  (&ship2, 0);
  ck_assert(!ship_equals_except_observers(&ship1, &ship2));
  
  ck_assert(ship_equals_except_observers (NULL,    NULL));
  ck_assert(!ship_equals_except_observers(&a_ship, NULL));
  ck_assert(!ship_equals_except_observers(NULL,    &a_ship));
}
END_TEST

START_TEST(test_ship_collision)
{
  ck_assert(is_collision_ship_with_ship(&a_ship, &a_ship));
  ck_assert(!is_collision_ship_with_other_player_ship(&a_ship, &a_ship));
  
  struct ship_t ship1, ship2;
  ship1 = a_ship;
  ship2 = a_ship;
  ship1.player_index = 1;
  ship2.player_index = 2;
  ck_assert(is_collision_ship_with_ship(&ship1, &ship2));
  ck_assert(is_collision_ship_with_other_player_ship(&ship1, &ship2));
}
END_TEST


Suite*
ship_suite(void)
{
  Suite* s = suite_create("Ship");
  
  TCase* tc_ship_life = tcase_create("Life");
  tcase_add_checked_fixture(tc_ship_life,
			    tests_setup_init, tests_setup_free);
  tcase_add_test(tc_ship_life,
		 test_ship_life_max);
  tcase_add_test(tc_ship_life,
		 test_ship_reduce_life);
  tcase_add_test(tc_ship_life,
		 test_ship_destroy);
  suite_add_tcase(s, tc_ship_life);
  
  TCase* tc_ship_update = tcase_create("Update");
  tcase_add_checked_fixture(tc_ship_update,
			    tests_setup_init, tests_setup_free);
  tcase_add_test(tc_ship_update,
		 test_ship_move_once_x);
  tcase_add_test(tc_ship_update,
		 test_ship_update_once_move_x);
  tcase_add_test(tc_ship_update,
		 test_ship_move_once_y);
  tcase_add_test(tc_ship_update,
		 test_ship_update_once_move_y);
  suite_add_tcase(s, tc_ship_update);
  
  TCase* tc_ship_others = tcase_create("Others");
  tcase_add_checked_fixture(tc_ship_others,
			    tests_setup_init, tests_setup_free);
  tcase_add_test(tc_ship_others,
		 test_ship_equals);
  tcase_add_test(tc_ship_others,
		 test_ship_collision);
  suite_add_tcase(s, tc_ship_others);
  
  return s;
}

int
main(void)
{
  size_t number_failed;
  Suite* s;
  SRunner* sr;
  
  s = ship_suite();
  sr = srunner_create(s);
  
  srunner_run_all(sr, CK_NORMAL);
  number_failed = srunner_ntests_failed(sr);
  srunner_free(sr);
  
  return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
